import { Record, List, Map } from 'immutable';
import { isForm } from '@design/constant/component-type';
import Document from './document';
import Style from './style';

const DEFAULTS = {
  document: undefined,
  style: undefined,
  selection: List(),
  data: undefined,
  undoStack: List(),
  redoStack: List()
};

class Value extends Record(DEFAULTS) {
  static create(attr = {}) {
    return Value.fromJSON(attr);
  }

  static fromJSON(attr = {}) {
    let {
      document = {},
      style = {},
      data = {},
      selection,
      undoStack,
      redoStack
    } = attr;
    document = Document.fromJSON(document);
    style = Style.fromJSON(style);
    return new Value({
      document,
      style,
      data: Map(data),
      selection,
      undoStack,
      redoStack
    });
  }

  toJS() {
    const { document, data, style } = this;
    return {
      document: document.toJS(),
      data: data.toJS(),
      style: style.toJS()
    };
  }

  setSelection(selection) {
    if (Array.isArray(selection)) {
      selection = List(selection);
    }

    if (!List.isList(selection)) {
      throw new Error(`selection must be string list, but got ${selection}`);
    }
    const value = this.set('selection', selection);
    return value;
  }

  insertNode(path, node) {
    let { document } = this;
    document = document.insertNodeByPath(path, node);
    const value = this.set('document', document);
    return value;
  }

  removeNode(path) {
    let { document, selection } = this;
    const node = document.getNode(path);
    selection = selection.filter(item => item.key !== node.key);
    document = document.removeNode(path);
    return this.set('document', document).set('selection', selection);
  }

  moveNode(source, target) {
    const document = this.document.moveNode(source, target);
    return this.set('document', document);
  }

  updateNode(key, node) {
    const document = this.document.replaceNode(key, node);
    const selectonIndex = this.selection.findIndex(item => item.key === key);
    const selection = this.selection.update(selectonIndex, () => node);
    return this.set('document', document).set('selection', selection);
  }

  isSelected(node) {
    return this.selection.contains(node);
  }

  getAllFormNodes() {
    const { document } = this;
    function getFormNodes(node) {
      let list = List();
      if (isForm(node.type)) {
        list = list.push(node);
      }
      if (List.isList(node.nodes)) {
        list = list.concat(node.nodes.flatMap(getFormNodes));
      }
      return list;
    }
    return getFormNodes(document);
  }
}

export default Value;
